package com.niodata.dp.monitor.mybatis;

import com.niodata.dp.mybatis.DpMybatisTypeHandler;
import io.github.lukehutch.fastclasspathscanner.FastClasspathScanner;
import io.github.lukehutch.fastclasspathscanner.matchprocessor.ImplementingClassMatchProcessor;
import io.github.lukehutch.fastclasspathscanner.scanner.ScanResult;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Modifier;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.ibatis.type.TypeHandlerRegistry;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.ResourceLoader;


/**
 * Monitor datasource config.
 */
@Configuration
@MapperScan(basePackages = {"com.niodata.dp.mybatis",
      "com.niodata.dp.monitor.components"},
      sqlSessionFactoryRef = "MonitorSqlSessionFactory")
public class MonitorDataSourceConfig {

  static final String PACKAGE = "com.niodata.dp.monitor.components;"
        + "com.niodata.dp.mybatis";
  @Value("${monitor.jdbcUrl}")
  private String jdbcUrl;

  @Value("${monitor.username}")
  private String username;
  @Value("${monitor.password}")
  private String password;
  @Value("${monitor.driverClassName}")
  private String driverClassName;

  /**
   * monitorDataSource.
   *
   * @return DataSource DataSource
   * @throws IOException IOException
   */
  @Bean(name = "MonitorDataSource")
  public DataSource monitorDataSource() throws IOException {
    ResourceLoader loader = new DefaultResourceLoader();
    InputStream inputStream = loader.getResource("classpath:application.properties")
          .getInputStream();
    Properties properties = new Properties();
    properties.load(inputStream);
    Set<Object> keys = properties.keySet();
    Properties dsproperties = new Properties();
    for (Object key : keys) {
      if (key.toString().startsWith("datasource")) {
        dsproperties.put(key.toString().replace("datasource.", ""), properties.get(key));
      }
    }
    dsproperties.put("driverClassName", driverClassName);
    dsproperties.put("jdbcUrl", jdbcUrl);
    dsproperties.put("username", username);
    dsproperties.put("password", password);
    HikariDataSourceFactory factory = new HikariDataSourceFactory();
    factory.setProperties(dsproperties);
    inputStream.close();
    return factory.getDataSource();
  }

  /**
   * monitorSqlSessionFactory.
   */
  @Bean(name = "MonitorSqlSessionFactory")
  public SqlSessionFactoryBean monitorSqlSessionFactory(
        @Qualifier("MonitorDataSource") DataSource monitorDataSource) throws Exception {
    SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean() {
      protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
        SqlSessionFactory factory = super.buildSqlSessionFactory();
        try {
          registerTypeHandler(factory);
        } catch (Exception e) {
          e.printStackTrace();
        }
        return factory;
      }
    };
    factoryBean.setDataSource(monitorDataSource);
    ResourceLoader loader = new DefaultResourceLoader();
    String resource = "classpath:mybatis-config.xml";
    factoryBean.setConfigLocation(loader.getResource(resource));
    factoryBean.setSqlSessionFactoryBuilder(new SqlSessionFactoryBuilder());
    return factoryBean;
  }

  /**
   * registerTypeHandler.
   */
  private void registerTypeHandler(SqlSessionFactory factory) throws Exception {
    TypeHandlerRegistry registry = factory.getConfiguration()
          .getTypeHandlerRegistry();
    String pkgs = PACKAGE;
    if (pkgs != null) {
      String[] allPkgs = pkgs.split(";");
      for (String pkg : allPkgs) {
        FastClasspathScanner scanner = new FastClasspathScanner(pkg);
        scanner.matchClassesImplementing(DpMybatisTypeHandler.class,
              new ImplementingClassMatchProcessor() {
                @Override
                public void processMatch(Class implementingClass) {
                }
              });
        ScanResult result = scanner.scan();
        List<String> classes = result
              .getNamesOfClassesImplementing(DpMybatisTypeHandler.class);
        for (String clazz : classes) {
          Class classEntity = Class.forName(clazz);
          if (Modifier.isAbstract(classEntity.getModifiers())) {
            continue;
          }
          DpMybatisTypeHandler handler = (DpMybatisTypeHandler)
                (Class.forName(clazz).newInstance());
          registry.register(handler.getJavaClass(), handler.getJdbcType(),
                handler.getTypeHandlerClass());
        }
      }
    }
  }
}
